package com.bocom.hairy.crab.base.common.utils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * @ClassName com.bocom.hairy.crab.base.common.utils.SymmetricEncryptionUtil <br/>
 * @Description: 对称加密算法工具类   <br/>
 * 支持AES、DES、3DES算法的加密与解密
 * @Author jiangshikun             <br/>
 * @Date 2025/9/3 下午4:07         <br/>
 * @Version V1.0
 * ----------------------------------------
 * （^-^）心即理，知行合--致良知 （^-^）
 * 邮 箱:duzhuai@live.cn
 * 地 址：北京市海淀区永澄北路2号
 **/

public class SymmetricEncryptionUtil {

    // 算法名称
    public static final String AES = "AES";
    public static final String DES = "DES";
    public static final String DESede = "DESede"; // 3DES

    // 算法模式与填充方式
    public static final String AES_CBC_PKCS5 = "AES/CBC/PKCS5Padding";
    public static final String DES_CBC_PKCS5 = "DES/CBC/PKCS5Padding";
    public static final String DESede_CBC_PKCS5 = "DESede/CBC/PKCS5Padding";

    /**
     * 生成指定算法的密钥
     * @param algorithm 算法名称(AES/DES/DESede)
     * @return Base64编码的密钥
     * @throws Exception 异常
     */
    public static String generateKey(String algorithm) throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);

        // 根据算法设置密钥长度
        if (algorithm.equals(AES)) {
            keyGenerator.init(128); // AES支持128/192/256位
        } else if (algorithm.equals(DES)) {
            keyGenerator.init(56);  // DES固定56位
        } else if (algorithm.equals(DESede)) {
            keyGenerator.init(168); // 3DES固定168位
        }

        SecretKey secretKey = keyGenerator.generateKey();
        return Base64.getEncoder().encodeToString(secretKey.getEncoded());
    }

    /**
     * 生成随机初始化向量(IV)
     * @param length 长度(字节)
     * @return Base64编码的IV
     */
    public static String generateIV(int length) {
        byte[] iv = new byte[length];
        new SecureRandom().nextBytes(iv);
        return Base64.getEncoder().encodeToString(iv);
    }

    /**
     * 加密方法
     * @param content 待加密内容
     * @param key 密钥(Base64编码)
     * @param iv 初始化向量(Base64编码，ECB模式不需要)
     * @param transformation 算法模式与填充方式
     * @return 加密后的Base64字符串
     * @throws Exception 异常
     */
    public static String encrypt(String content, String key, String iv, String transformation) throws Exception {
        // 获取算法名称
        String algorithm = transformation.split("/")[0];

        // 转换密钥
        SecretKey secretKey = getSecretKey(key, algorithm);

        // 初始化加密器
        Cipher cipher = Cipher.getInstance(transformation);
        if (iv != null && !iv.isEmpty()) {
            // CBC等模式需要IV
            IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(iv));
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
        } else {
            // ECB模式不需要IV
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        }

        // 执行加密
        byte[] encryptedData = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * 解密方法
     * @param encryptedContent 加密后的Base64字符串
     * @param key 密钥(Base64编码)
     * @param iv 初始化向量(Base64编码，ECB模式不需要)
     * @param transformation 算法模式与填充方式
     * @return 解密后的原始内容
     * @throws Exception 异常
     */
    public static String decrypt(String encryptedContent, String key, String iv, String transformation) throws Exception {
        // 获取算法名称
        String algorithm = transformation.split("/")[0];

        // 转换密钥
        SecretKey secretKey = getSecretKey(key, algorithm);

        // 初始化解密器
        Cipher cipher = Cipher.getInstance(transformation);
        if (iv != null && !iv.isEmpty()) {
            // CBC等模式需要IV
            IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(iv));
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
        } else {
            // ECB模式不需要IV
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
        }

        // 执行解密
        byte[] decryptedData = cipher.doFinal(Base64.getDecoder().decode(encryptedContent));
        return new String(decryptedData, StandardCharsets.UTF_8);
    }

    /**
     * 将Base64编码的密钥转换为SecretKey对象
     * @param key Base64编码的密钥
     * @param algorithm 算法名称
     * @return SecretKey对象
     * @throws Exception 异常
     */
    private static SecretKey getSecretKey(String key, String algorithm) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(key);

        switch (algorithm) {
            case DES:
                DESKeySpec desKeySpec = new DESKeySpec(keyBytes);
                SecretKeyFactory desFactory = SecretKeyFactory.getInstance(DES);
                return desFactory.generateSecret(desKeySpec);
            case DESede:
                DESedeKeySpec desedeKeySpec = new DESedeKeySpec(keyBytes);
                SecretKeyFactory desedeFactory = SecretKeyFactory.getInstance(DESede);
                return desedeFactory.generateSecret(desedeKeySpec);
            case AES:
            default:
                return new SecretKeySpec(keyBytes, algorithm);
        }
    }

    /**
     * 测试方法
     */
    public static void main(String[] args) {
        try {
            String originalContent = "这是一段测试文本，包含中英文和特殊字符!@#$%^&*()";
            System.out.println("原始内容: " + originalContent);
            System.out.println("----------------------------------------");

            // 测试AES算法
            String aesKey = generateKey(AES);
            String aesIV = generateIV(16); // AES的IV长度固定为16字节
            System.out.println("AES密钥: " + aesKey);
            System.out.println("AES-IV: " + aesIV);

            String aesEncrypted = encrypt(originalContent, aesKey, aesIV, AES_CBC_PKCS5);
            System.out.println("AES加密后: " + aesEncrypted);

            String aesDecrypted = decrypt(aesEncrypted, aesKey, aesIV, AES_CBC_PKCS5);
            System.out.println("AES解密后: " + aesDecrypted);
            System.out.println("AES解密验证: " + originalContent.equals(aesDecrypted));
            System.out.println("----------------------------------------");

            // 测试DES算法
            String desKey = generateKey(DES);
            String desIV = generateIV(8); // DES的IV长度固定为8字节
            System.out.println("DES密钥: " + desKey);
            System.out.println("DES-IV: " + desIV);

            String desEncrypted = encrypt(originalContent, desKey, desIV, DES_CBC_PKCS5);
            System.out.println("DES加密后: " + desEncrypted);

            String desDecrypted = decrypt(desEncrypted, desKey, desIV, DES_CBC_PKCS5);
            System.out.println("DES解密后: " + desDecrypted);
            System.out.println("DES解密验证: " + originalContent.equals(desDecrypted));
            System.out.println("----------------------------------------");

            // 测试3DES算法
            String desedeKey = generateKey(DESede);
            String desedeIV = generateIV(8); // 3DES的IV长度固定为8字节
            System.out.println("3DES密钥: " + desedeKey);
            System.out.println("3DES-IV: " + desedeIV);

            String desedeEncrypted = encrypt(originalContent, desedeKey, desedeIV, DESede_CBC_PKCS5);
            System.out.println("3DES加密后: " + desedeEncrypted);

            String desedeDecrypted = decrypt(desedeEncrypted, desedeKey, desedeIV, DESede_CBC_PKCS5);
            System.out.println("3DES解密后: " + desedeDecrypted);
            System.out.println("3DES解密验证: " + originalContent.equals(desedeDecrypted));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

